package vip.xiaonuo.auth.modular.third.service.impl;

import cn.hutool.crypto.digest.MD5;
import vip.xiaonuo.auth.modular.login.enums.AuthDeviceTypeEnum;
import vip.xiaonuo.auth.modular.login.param.UniappLoginCodeParam;
import vip.xiaonuo.auth.modular.login.param.WeChatServerVerifyParam;
import vip.xiaonuo.auth.modular.third.service.SSOAuthService;
import vip.xiaonuo.auth.modular.login.service.AuthService;
import vip.xiaonuo.sys.api.SysUserApi;
import vip.xiaonuo.dev.api.DevConfigApi;
import vip.xiaonuo.common.exception.CommonException;

import java.io.IOException;
import java.net.ConnectException;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;

/**
 * SSO登录Service接口实现类
 *
 * @author Blurryface
 * @date 2025/6/7
 */
@Service
@Slf4j
public class SSOAuthServiceImpl implements SSOAuthService {
    private static final String GRANT_TYPE              = "authorization_code";
    private static final String RESPONSE_TYPE           = "code";
    public  static final String SSO_HOST                = "https://sso.scnu.edu.cn/AccountService";
    public  static final String SSO_AUTH                = SSO_HOST + "/openapi/auth.html";
    public  static final String SSO_USERINFO            = SSO_HOST + "/openapi/userinfo.html";
    public  static final String SSO_TOKEN               = SSO_HOST + "/openapi/token.html";
    private static final String CLIENT_ID               = "678385ffaec2c4b1a632e755e4a2a38b";
    private static final String CLIENT_SECRET           = "04jlkHflx2Dp4FlKp90wJe3xh76oPAUmtfZdbJcfEd4EvH9kezlBhwdwcNviznGF";
    private static final String REDIRECT_URL            = "https://soulmeet.scnu.edu.cn/callback";

    private static final String WECHAT_SSO_Reg          = SSO_HOST + "/openapi/wechat_applet_reg.html";
    private static final String WECHAT_SSO_Login        = SSO_HOST + "/openapi/wechat_applet.html";
    private static final String WECHAT_APPID            = "wx12f95b7ff470609e";
    private static final String WECHAT_SECRET           = "0c58703894e753a3842ef83c1fb064c5";
    private static final String WECHAT_APP_NAME         = "心院有约";

    private static final String WECHAT_OFFICICAL_ID                 = "wx322de91d04b82073"; // 微信公众号id
    private static final String WECHAT_OFFICICAL_TOKEN              = "SOULMEETSCNUEDUCN2025"; // 微信公众号 由开发者可以任意填写的 Token
    private static final String WECHAT_OFFICICAL_ENCODEINGAESKEY    = "vMBr2dSGb4ufvtDdaUjJFQYy99IUSajDz6zheEecXdi"; // 微信公众号 由开发者随机生成的 EncodingAESKey 将用作消息体加解密密钥

    @Resource
    private SysUserApi sysUserApi;

    @Resource
    private AuthService authService;

    @Resource
    private DevConfigApi devConfigApi;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public String scnuSSOInit() {
        return UrlBuilder.of(SSO_AUTH)
                .addQuery("client_id", CLIENT_ID)
                .addQuery("response_type", RESPONSE_TYPE)
                .addQuery("redirect_url", REDIRECT_URL)
                .build();
    }

    @Override
    public String scnuSSOAuth(String code) throws IOException { // 废弃
        if (code == null || code.isEmpty()) {
            log.error("code获取失败");
            throw new ConnectException("code获取失败");
        }
        // log.info("code获取成功:{}", code);

        // 请求access_token
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("code", code);
        paramMap.put("client_id", CLIENT_ID);
        paramMap.put("client_secret", CLIENT_SECRET);
        paramMap.put("grant_type", GRANT_TYPE);

        // 发送请求
        String accessTokenResult = HttpUtil.post(SSO_TOKEN, paramMap);

        // 解析结果
        JSONObject json = JSONUtil.parseObj(accessTokenResult);
        if(!"00".equals(json.getStr("msgcode"))) {
            log.error("accessToken获取失败，状态信息：{}", json.getStr("msgtext"));
            throw new CommonException("accessToken获取失败！");
        }
        //log.info("accessToken获取成功：" + accessTokenResult);
        String accessToken = json.getStr("access_token");

        // 用access_token请求用户信息
        HashMap<String, Object> paramMap2 = new HashMap<>();
        paramMap2.put("access_token", accessToken);
        String userInfoResult = HttpUtil.post(SSO_USERINFO, paramMap2);

        // 解析用户结果
        JSONObject userObject = JSONUtil.parseObj(userInfoResult);
        if(!"00".equals(userObject.getStr("msgcode"))) {
            log.error("用户信息获取失败，状态信息：{}", userObject.getStr("msgtext"));
            throw new CommonException("用户信息获取失败！");
        }
        log.info("用户信息获取成功：{}", userInfoResult);

        String empNo = userObject.getStr("account");

        // 校验工号长度
        if(empNo.length() != 8) {
            log.info("职工登录");
        }

        // 拿到工号后进行校验
        int isHasUser = sysUserApi.isHasUserBySSOLogin(userObject);

        if(isHasUser == 0){
            // 没有这个用户，注册
            sysUserApi.addUserBySSOLogin(userObject);
        }

        return authService.doLoginByEmpNo(empNo, AuthDeviceTypeEnum.PC.getValue());

    }

    @Override
    public Boolean SCNUSSOWeChatRegister() {
        String flag = devConfigApi.getValueByKey("ALLOW_SCNUSSO_WECHAT_REGISTER");
        if("false".equals(flag)) {
            return false;
        }

        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("applet_id", WECHAT_APPID);
        paramMap.put("applet_secret", WECHAT_SECRET);
        paramMap.put("applet_name", WECHAT_APP_NAME);
        paramMap.put("client_id", CLIENT_ID);

        long timestamp = System.currentTimeMillis();
        paramMap.put("timestamp", timestamp); //当前时间戳，13 位精确到毫秒

        String signStr = WECHAT_APPID + WECHAT_APP_NAME + WECHAT_SECRET + CLIENT_ID + timestamp + CLIENT_SECRET;
        String sign = MD5.create().digestHex(signStr);
        paramMap.put("sign", sign); //MD5(app_id+app_name+app_secret+client_id+timestamp+client_secret)

        // 发送请求
        String SSOWeChatRegResult= HttpUtil.post(WECHAT_SSO_Reg, paramMap);

        // 解析结果
        JSONObject json = JSONUtil.parseObj(SSOWeChatRegResult);
        if(!"0".equals(json.getStr("msgcode"))) {
            log.error("SCNU SSO注册WeChat小程序失败，状态信息：{}", json.getStr("msgtext"));
            throw new CommonException("SCNU SSO注册WeChat小程序失败！");
        }

        return true;
    }

    @Override
    public String SCNUSSOWeChatLogin(String code) throws IOException {
        String accessToken = getSSOAccessToken(code);

        // 用access_token请求用户信息
        HashMap<String, Object> paramMap2 = new HashMap<>();
        paramMap2.put("access_token", accessToken);
        String userInfoResult = HttpUtil.post(SSO_USERINFO, paramMap2);

        // 解析用户结果
        JSONObject userObject = JSONUtil.parseObj(userInfoResult);
        if(!"00".equals(userObject.getStr("msgcode"))) {
            log.error("用户信息获取失败，状态信息：{}", userObject.getStr("msgtext"));
            throw new CommonException("用户信息获取失败！");
        }
        log.info("用户信息获取成功：{}", userInfoResult);

        String empNo = userObject.getStr("account");

        // 校验工号长度
        if(empNo.length() != 8) {
            log.info("职工登录");
        }

        // 拿到工号后进行校验
        int isHasUser = sysUserApi.isHasUserBySSOLogin(userObject);

        if(isHasUser == 0){
            // 没有这个用户，注册
            sysUserApi.addUserBySSOLogin(userObject);
        }

        return authService.doLoginByEmpNo(empNo, AuthDeviceTypeEnum.MINI.getValue());
    }

    private String getSSOAccessToken(String code) throws IOException {
        // 请求access_token
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("code", code);
        paramMap.put("client_id", CLIENT_ID);
        paramMap.put("client_secret", CLIENT_SECRET);
        paramMap.put("grant_type", GRANT_TYPE);

        // 发送请求
        String accessTokenResult = HttpUtil.post(SSO_TOKEN, paramMap);

        // 解析结果
        JSONObject json = JSONUtil.parseObj(accessTokenResult);
        if(!"00".equals(json.getStr("msgcode"))) {
            log.error("accessToken获取失败，状态信息：{}", json.getStr("msgtext"));
            throw new CommonException("accessToken获取失败！");
        }
        return json.getStr("access_token");
    }

    /**
     * 微信公众号验证服务器地址的有效性
     * https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
     */
    @Override
    public String verifyWeChatServer(WeChatServerVerifyParam param) {
        String[] arr = new String[]{WECHAT_OFFICICAL_TOKEN, param.getTimestamp(), param.getNonce()};
        java.util.Arrays.sort(arr);

        StringBuilder content = new StringBuilder();
        for (String s : arr) {
            content.append(s);
        }
        String tmpStr = cn.hutool.crypto.SecureUtil.sha1(content.toString());

        if (tmpStr.equals(param.getSignature())) {
            return param.getEchostr();
        } else {
            return null;
        }
    }

    /**
     * 存储Uniapp Login Code 到 Redis
     */
    @Override
    public Boolean storeUniLoginCode(UniappLoginCodeParam param) {
        try {
            String key = "uniapp:login:code:" + param.getUniappLoginCode();
            String value = param.getSCNUSSOCode();

            stringRedisTemplate.opsForValue().set(key, value, 300, TimeUnit.SECONDS);
            
            log.info("成功存储Uniapp Login Code到Redis，key: {}", key);
            return true;
        } catch (Exception e) {
            log.error("存储Uniapp Login Code到Redis失败", e);
            return false;
        }
    }

    /**
     * 从Redis中获取Uniapp Login Code
     */
    @Override
    public String getUniLoginCode(UniappLoginCodeParam param) {
        try {
            String key = "uniapp:login:code:" + param.getUniappLoginCode();
            String value = stringRedisTemplate.opsForValue().get(key);
            
            if (value != null) {
                log.info("成功从Redis获取Uniapp Login Code，key: {}", key);
                // 获取后删除，确保code的一次性使用特性
                stringRedisTemplate.delete(key);
            } else {
                log.warn("从Redis获取Uniapp Login Code失败，key不存在或已过期: {}", key);
            }
            
            return value;
        } catch (Exception e) {
            log.error("从Redis获取Uniapp Login Code失败", e);
            return null;
        }
    }

}
